// @(#)root/treeplayer:$Id: TTreePlayer.cxx 31752 2009-12-10 07:50:15Z brun $

// converter CERN-ROOT-TTree -> ocaml
// original ROOT source was corrected to allow 
// generation of ocaml-readable files

#include <string.h>
#include <stdio.h>

#include "snprintf.h"

#include "Riostream.h"
#include "TTreePlayer.h"
#include "TROOT.h"
#include "TSystem.h"
#include "TFile.h"
#include "TEventList.h"
#include "TEntryList.h"
#include "TBranchObject.h"
#include "TBranchElement.h"
#include "TStreamerInfo.h"
#include "TStreamerElement.h"
#include "TLeafObject.h"
#include "TLeafF.h"
#include "TLeafD.h"
#include "TLeafC.h"
#include "TLeafB.h"
#include "TLeafI.h"
#include "TLeafS.h"
#include "TMath.h"
#include "TH2.h"
#include "TH3.h"
#include "TPolyMarker.h"
#include "TPolyMarker3D.h"
#include "TDirectory.h"
#include "TClonesArray.h"
#include "TClass.h"
#include "TVirtualPad.h"
#include "TProfile.h"
#include "TProfile2D.h"
#include "TTreeFormula.h"
#include "TTreeFormulaManager.h"
#include "TStyle.h"
#include "Foption.h"
#include "TTreeResult.h"
#include "TTreeRow.h"
#include "TPrincipal.h"
#include "TChain.h"
#include "TChainElement.h"
#include "TF1.h"
#include "TH1.h"
#include "TVirtualFitter.h"
#include "TEnv.h"
#include "THLimitsFinder.h"
#include "TSelectorDraw.h"
#include "TSelectorEntries.h"
#include "TPluginManager.h"
#include "TObjString.h"
#include "TTreeProxyGenerator.h"
#include "TTreeIndex.h"
#include "TChainIndex.h"
#include "TRefProxy.h"
#include "TRefArrayProxy.h"
#include "TVirtualMonitoring.h"
#include "TTreeCache.h"
#include "TStyle.h"

#include "HFitInterface.h"
#include "Foption.h"
#include "Fit/UnBinData.h"
#include "Math/MinimizerOptions.h"

R__EXTERN Foption_t Foption;
R__EXTERN  TTree *gTree;

TVirtualFitter *tFitter=0;

void add(std::map<std::string,std::vector<string> > &m, std::string K, 
	std::string v1)
{
  std::vector<string> t;
  t.push_back(v1);
  m.insert( std::make_pair(K, t));
}

char *to_lower(const char *name)
{
  char * o_name;
  o_name = new char[1+strlen(name)];
  strcpy(o_name,name);
  if((int)name[0]<97) // upper case
  {
    o_name[0] = (char) ((int)(name[0]) + 32);
  }
  else // letter case
  {
  }
  printf("branch: %s\n",o_name);
  return o_name;
}

char *get_o_name(const char *name)
{
  char * o_name;
  if((int)name[0]<97) // upper case
  {
    o_name = new char[1+1+strlen(name)];
    o_name[0]='_';
    strcpy(&o_name[1],name);
  }
  else // letter case
  {
    o_name = new char[1+strlen(name)];
    strcpy(o_name,name);
  }
  printf("branch: %s\n",o_name);
  return o_name;
} 

bool first_letter_capital = true;

char *get_name(const char *name)
{
  if (first_letter_capital)
      return get_o_name(name);
  else
      return to_lower(name);
}

char *get_attr(const char *name)
{
  char *_name = get_name(name);
  int n = strlen(_name);
  char *result = new char[n+20];
  sprintf(result,"mlname(%s),",_name);
  return result;
}

void add_b(std::vector<string> &v, const char *s)
{
  bool found = false;
//  char *s1 = to_lower(s);
  for(int i = 0; i < v.size(); i++) {
      if(v[i]==s) found = true;
  }
  if(!found)
      v.push_back(s);
}

//char * get_type(int out, std::string in)
char * get_type(std::string in)
{
    int out=0;
    std::map<std::string,std::vector<string> > m;
    //order: ROOT type, ocaml type, ocaml bigarray element type, 
    // bigarray result type, bigarray init type,
    // C interface bigarray, ocaml init value, C value, convertion condition 
    add(m,"Float_t", "float");
    add(m,"Double_t","double");
    add(m,"Int_t",   "int");
    add(m,"Long_t",  "long");
    add(m,"UInt_t",  "unsigned int");
    add(m,"Long64_t","long long int" );
    add(m,"ULong64_t","unsigned long long int" );
    add(m,"Bool_t","boolean" );
    std::vector<std::string> t = m[in];
//    printf("demand %s\n",in.c_str());
//    printf("result: %d\n",t.size());
    if(t.size()==0) { 
      printf("unknow type demanded: %s\n",in.c_str());
      exit(5);
    }
	/*return (char*)"";*/
//    if(!strcmp(t[0].c_str(),"")) {return "";};
//    printf("obtaining %s:\n",in.c_str());
//    printf("result [%d]: %s\n",out,t[out].c_str());
//    printf("allocated: %x %x\n",&t,t[out].c_str());
    char *result = new char[1+strlen(t[out].c_str())];
    strcpy(result,t[out].c_str());
//    return (char*)(t[out].c_str());
    return result;
}

void obj_add_val(FILE *fp, const char*name)
{
    char * _name = get_name(name);
    fprintf(fp,"  val %s = r.%s\n",_name,_name);
    fprintf(fp,"  method %s = %s\n",_name,_name);
}

static TString R__GetBranchPointerName(TLeaf *leaf)
{
   // Return the name of the branch pointer needed by MakeClass/MakeSelector

   TLeaf *leafcount = leaf->GetLeafCount();
   TBranch *branch = leaf->GetBranch();

   TString branchname( branch->GetName() );

   if ( branch->GetNleaves() <= 1 ) {
       if (branch->IsA() != TBranchObject::Class()) {
         if (!leafcount) {
            TBranch *mother = branch->GetMother();
            const char* ltitle = leaf->GetTitle();
            if (mother && mother!=branch) {
               branchname = mother->GetName();
               if (branchname[branchname.Length()-1]!='.') {
                  branchname += ".";
               }
               if (strncmp(branchname.Data(),ltitle,branchname.Length())==0) {
                  branchname = "";
               }
            } else {
               branchname = "";
            }
            branchname += ltitle;
         }
      }
   }
   char *bname = (char*)branchname.Data();
   char *twodim = (char*)strstr(bname,"[");
   if (twodim) *twodim = 0;
   while (*bname) {
      if (*bname == '.') *bname='_';
      if (*bname == ',') *bname='_';
      if (*bname == ':') *bname='_';
      if (*bname == '<') *bname='_';
      if (*bname == '>') *bname='_';
      bname++;
   }
   return branchname;
}

//______________________________________________________________________________
//Int_t TTreePlayer::MakeClass(const char *classname, const char *option)
Int_t MakeClass(TTree *fTree, const char *classname, TString name_idl, const char *option)
{
   TString opt = option;
   opt.ToLower();

   // Connect output files
   if (!classname) classname = fTree->GetName();

   // file No 1
   TString thead;
   thead.Form("ttree_declaration.h");
   FILE *fp = fopen(thead, "w");
   if (!fp) {
      Error("MakeClass","cannot open output file %s", thead.Data());
      return 3;
   }
   std::vector<string> v_val; // all values
   // indices, which will be excluded from ocaml record:
   std::vector<string> v_ind; 
   char ind_buff[200];

   FILE *fp_idl=fopen(name_idl,"w");
   FILE *fp_ocl=fopen("oclassdef.ml","w");
   fprintf(fp_ocl,"class ttree_class_decl r = object\n");

   TString treefile;
   if (fTree->GetDirectory() && fTree->GetDirectory()->GetFile()) {
      treefile = fTree->GetDirectory()->GetFile()->GetName();
   } else {
      treefile = "Memory Directory";
   }
   // In the case of a chain, the GetDirectory information usually does
   // pertain to the Chain itself but to the currently loaded tree.
   // So we can not rely on it.
   Bool_t ischain = fTree->InheritsFrom("TChain");
   Bool_t isHbook = fTree->InheritsFrom("THbookTree");
   if (isHbook)
      treefile = fTree->GetTitle();

//======================Generate classname.h=====================
   // Print header
   TObjArray *leaves = fTree->GetListOfLeaves();
   Int_t nleaves = leaves ? leaves->GetEntriesFast() : 0;
   TDatime td;
   fprintf(fp,"//////////////////////////////////////////////////////////\n");
   fprintf(fp,"// This class has been automatically generated on\n");
   fprintf(fp,"// my wrapper\n");
   fprintf(fp,"// %s by ROOT version %s\n",td.AsString(),gROOT->GetVersion());
   if (!ischain) {
      fprintf(fp,"// from TTree %s/%s\n",fTree->GetName(),fTree->GetTitle());
      fprintf(fp,"// found on file: %s\n",treefile.Data());
   } else {
      fprintf(fp,"// from TChain %s/%s\n",fTree->GetName(),fTree->GetTitle());
   }
   fprintf(fp,"//////////////////////////////////////////////////////////\n");
   fprintf(fp,"\n");
   fprintf(fp,"#ifndef %s_h\n",classname);
   fprintf(fp,"#define %s_h\n",classname);
   fprintf(fp,"\n");
   fprintf(fp,"#include <TROOT.h>\n");
   fprintf(fp,"#include <TChain.h>\n");
   fprintf(fp,"#include <TFile.h>\n");
   if (isHbook) fprintf(fp,"#include <THbookFile.h>\n");
   if (opt.Contains("selector")) fprintf(fp,"#include <TSelector.h>\n");

// First loop on all leaves to generate dimension declarations
   Int_t len, lenb,l;
   char blen[1024];
   char *bname;
   Int_t *leaflen = new Int_t[nleaves];
   TObjArray *leafs = new TObjArray(nleaves);
   for (l=0;l<nleaves;l++) {
      TLeaf *leaf = (TLeaf*)leaves->UncheckedAt(l);
      leafs->AddAt(new TObjString(leaf->GetName()),l);
      leaflen[l] = leaf->GetMaximum();
   }
   if (ischain) {
      // In case of a chain, one must find the maximum dimension of each leaf
      // One must be careful and not assume that all Trees in the chain
      // have the same leaves and in the same order!
      TChain *chain = (TChain*)fTree;
      Int_t ntrees = chain->GetNtrees();
      for (Int_t file=0;file<ntrees;file++) {
         Long64_t first = chain->GetTreeOffset()[file];
         chain->LoadTree(first);
         for (l=0;l<nleaves;l++) {
            TObjString *obj = (TObjString*)leafs->At(l);
            TLeaf *leaf = chain->GetLeaf(obj->GetName());
            if (leaf) {
               leaflen[l] = TMath::Max(leaflen[l],leaf->GetMaximum());
            }
         }
      }
      chain->LoadTree(0);
   }

   leaves = fTree->GetListOfLeaves();
   for (l=0;l<nleaves;l++) {
      TLeaf *leaf = (TLeaf*)leaves->UncheckedAt(l);
      strcpy(blen,leaf->GetName());
      bname = &blen[0];
      while (*bname) {
         if (*bname == '.') *bname='_';
         if (*bname == ',') *bname='_';
         if (*bname == ':') *bname='_';
         if (*bname == '<') *bname='_';
         if (*bname == '>') *bname='_';
         bname++;
      }
      lenb = strlen(blen);
      if (blen[lenb-1] == '_') {
         blen[lenb-1] = 0;
         len = leaflen[l];
         if (len <= 0) len = 1;
         fprintf(fp,"   const Int_t kMax%s = %d;\n",blen,len);
         fprintf(fp_idl,"const int kMax%s = %d;\n",blen,len);
      }
   }
   delete [] leaflen;
   leafs->Delete();
   delete leafs;

// second loop on all leaves to generate type declarations
   fprintf(fp_idl,"struct ttree_decl {\n");
   fprintf(fp,"\n");
   if (opt.Contains("selector")) {
      fprintf(fp,"class %s : public TSelector {\n",classname);
      fprintf(fp,"public :\n");
      fprintf(fp,"   TTree          *fChain;   //!pointer to the analyzed TTree or TChain\n");
   } else {
      fprintf(fp,"class %s {\n",classname);
      fprintf(fp,"public :\n");
      fprintf(fp,"   TTree          *fChain;   //!pointer to the analyzed TTree or TChain\n");
      fprintf(fp,"   Int_t           fCurrent; //!current Tree number in a TChain\n");
   }
   fprintf(fp,"\n   // Declaration of leaf types\n");
   TLeaf *leafcount;
   TLeafObject *leafobj;
   TBranchElement *bre=0;
   const char *headOK  = "   ";
   const char *headcom = " //";
   const char *head;
   char branchname[1024];
   char aprefix[1024];
   TObjArray branches(100);
   TObjArray mustInit(100);
   TObjArray mustInitArr(100);
   mustInitArr.SetOwner(kFALSE);
   Int_t *leafStatus = new Int_t[nleaves];
   for (l=0;l<nleaves;l++) {
      Int_t kmax = 0;
      head = headOK;
      leafStatus[l] = 0;
      TLeaf *leaf = (TLeaf*)leaves->UncheckedAt(l);
      len = leaf->GetLen(); if (len<=0) len = 1;
      leafcount =leaf->GetLeafCount();
      TBranch *branch = leaf->GetBranch();
      branchname[0] = 0;
      strcpy(branchname,branch->GetName());
      strcpy(aprefix,branch->GetName());
      if (!branches.FindObject(branch)) branches.Add(branch);
      else leafStatus[l] = 1;
      if ( branch->GetNleaves() > 1) {
         // More than one leaf for the branch we need to distinguish them
         strcat(branchname,".");
         strcat(branchname,leaf->GetTitle());
         if (leafcount) {
            // remove any dimension in title
            char *dim =  (char*)strstr(branchname,"["); if (dim) dim[0] = 0;
         }
      } else {
         strcpy(branchname,branch->GetName());
      }
      char *twodim = (char*)strstr(leaf->GetTitle(),"][");
      bname = branchname;
      while (*bname) {
         if (*bname == '.') *bname='_';
         if (*bname == ',') *bname='_';
         if (*bname == ':') *bname='_';
         if (*bname == '<') *bname='_';
         if (*bname == '>') *bname='_';
         bname++;
      }
      if (branch->IsA() == TBranchObject::Class()) {
         if (branch->GetListOfBranches()->GetEntriesFast()) {leafStatus[l] = 1; continue;}
         leafobj = (TLeafObject*)leaf;
         if (!leafobj->GetClass()) {leafStatus[l] = 1; head = headcom;}
	 fprintf(fp," //:1\n");
         fprintf(fp,"%s%-15s *%s;\n",head,leafobj->GetTypeName(), leafobj->GetName());
         if (leafStatus[l] == 0) mustInit.Add(leafobj);
         continue;
      }
      if (leafcount) {
         len = leafcount->GetMaximum();
         if (len<=0) len = 1;
         strcpy(blen,leafcount->GetName());
         bname = &blen[0];
         while (*bname) {
            if (*bname == '.') *bname='_';
            if (*bname == ',') *bname='_';
            if (*bname == ':') *bname='_';
            if (*bname == '<') *bname='_';
            if (*bname == '>') *bname='_';
            bname++;
         }
         lenb = strlen(blen);
         if (blen[lenb-1] == '_') {blen[lenb-1] = 0; kmax = 1;}
         else                     sprintf(blen,"%d",len);
      }
      if (branch->IsA() == TBranchElement::Class()) {
         bre = (TBranchElement*)branch;
         if (bre->GetType() != 3 && bre->GetType() != 4
             && bre->GetStreamerType() <= 0 && bre->GetListOfBranches()->GetEntriesFast()) {
            leafStatus[l] = 0;
         }
         if (bre->GetType() == 3 || bre->GetType() == 4) {
	    fprintf(fp," //:2\n");
            fprintf(fp,"   %-15s %s_;\n","Int_t", branchname);
	    fprintf(fp_idl,"  [%s] %s %s_;\n",get_attr(branchname),get_type("Int_t"),branchname);
	    add_b(v_val,branchname);
            continue;
         }
         if (bre->IsBranchFolder()) {
	    fprintf(fp," //:3\n");
            fprintf(fp,"   %-15s *%s;\n",bre->GetClassName(), branchname);
            mustInit.Add(bre);
            continue;
         } else {
            if (branch->GetListOfBranches()->GetEntriesFast()) {leafStatus[l] = 1;}
         }
         if (bre->GetStreamerType() < 0) {
            if (branch->GetListOfBranches()->GetEntriesFast()) {
               fprintf(fp," //:4\n");
               fprintf(fp,"%s%-15s *%s;\n",headcom,bre->GetClassName(), bre->GetName());
            } else {
               fprintf(fp," //:5\n");
               fprintf(fp,"%s%-15s *%s;\n",head,bre->GetClassName(), bre->GetName());
               mustInit.Add(bre);
            }
            continue;
         }
         if (bre->GetStreamerType() == 0) {
            if (!TClass::GetClass(bre->GetClassName())->GetClassInfo()) {leafStatus[l] = 1; head = headcom;}
	    fprintf(fp," //:6\n");
            fprintf(fp,"%s%-15s *%s;\n",head,bre->GetClassName(), bre->GetName());
            if (leafStatus[l] == 0) mustInit.Add(bre);
            continue;
         }
         if (bre->GetStreamerType() > 60) {
            TClass *cle = TClass::GetClass(bre->GetClassName());
            if (!cle) {leafStatus[l] = 1; continue;}
            if (bre->GetStreamerType() == 66) leafStatus[l] = 0;
            char brename[256];
            strcpy(brename,bre->GetName());
            char *bren = brename;
            char *adot = strrchr(bren,'.');
            if (adot) bren = adot+1;
            char *brack = strchr(bren,'[');
            if (brack) *brack = 0;
            TStreamerElement *elem = (TStreamerElement*)cle->GetStreamerInfo()->GetElements()->FindObject(bren);
            if (elem) {
               if (elem->IsA() == TStreamerBase::Class()) {leafStatus[l] = 1; continue;}
               if (!TClass::GetClass(elem->GetTypeName())) {leafStatus[l] = 1; continue;}
               if (!TClass::GetClass(elem->GetTypeName())->GetClassInfo()) {leafStatus[l] = 1; head = headcom;}
               if (leafcount) {
		   fprintf(fp," //:7\n");
		   fprintf(fp,"%s%-15s %s[kMax%s];\n",head,elem->GetTypeName(), branchname,blen);
	       }
               else           {
		   fprintf(fp," //:8\n");
		   fprintf(fp,"%s%-15s %s;\n",head,elem->GetTypeName(), branchname);
	       }
            } else {
               if (!TClass::GetClass(bre->GetClassName())->GetClassInfo()) {leafStatus[l] = 1; head = headcom;}
	       fprintf(fp," //:9\n");
               fprintf(fp,"%s%-15s %s;\n",head,bre->GetClassName(), branchname);
            }
            continue;
         }
      }
      if (strlen(leaf->GetTypeName()) == 0) {leafStatus[l] = 1; continue;}
      if (leafcount) {
         //len = leafcount->GetMaximum();
         //strcpy(blen,leafcount->GetName());
         //bname = &blen[0];
         //while (*bname) {if (*bname == '.') *bname='_'; bname++;}
         //lenb = strlen(blen);
         //Int_t kmax = 0;
         //if (blen[lenb-1] == '_') {blen[lenb-1] = 0; kmax = 1;}
         //else                     sprintf(blen,"%d",len);

         const char *stars = " ";
         if (bre && bre->GetBranchCount2()) {
            stars = "*";
         }
         // Dimensions can be in the branchname for a split Object with a fix length C array.
         // Theses dimensions HAVE TO be placed after the dimension explicited by leafcount
         char *dimensions = 0;
         char *dimInName = (char*) strstr(branchname,"[");
         if ( twodim || dimInName ) {
            int dimlen = 0;
            if (dimInName) dimlen += strlen(dimInName) + 1;
            if (twodim)    dimlen += strlen(twodim) + 1;
            dimensions = new char[dimlen];
            if (dimInName) {
               strcpy(dimensions,dimInName);
               dimInName[0] = 0; // terminate branchname before the array dimensions.
            } else dimensions[0] = 0;
            if (twodim) strcat(dimensions,(char*)(twodim+1));
         }
         const char* leafcountName = leafcount->GetName();
         char b2len[1024];
         if (bre && bre->GetBranchCount2()) {
            TLeaf * l2 = (TLeaf*)bre->GetBranchCount2()->GetListOfLeaves()->At(0);
            strcpy(b2len,l2->GetName());
            bname = &b2len[0];
            while (*bname) {
               if (*bname == '.') *bname='_';
               if (*bname == ',') *bname='_';
               if (*bname == ':') *bname='_';
               if (*bname == '<') *bname='_';
               if (*bname == '>') *bname='_';
               bname++;
            }
            leafcountName = b2len;
         }
         if (dimensions) {
	    fprintf(fp," //:10\n");
            if (kmax) {
		fprintf(fp,"   %-14s %s%s[kMax%s]%s;   //[%s]\n",leaf->GetTypeName(), stars,
                              branchname,blen,dimensions,leafcountName);
		fprintf(fp_idl," [%s size_is(kMax%s,%s)], %s %s%s[kMax%s]%s;   //[%s]\n",
			get_attr(branchname),
			blen,dimensions,
			get_type(leaf->GetTypeName()), stars,
			branchname,blen,dimensions,leafcountName);
		add_b(v_val,branchname);
		sprintf(ind_buff,"kMax%s",blen);
		add_b(v_ind,ind_buff); add_b(v_ind,dimensions);
	    }
            else      {
		fprintf(fp,"   %-14s %s%s[%d]%s;   //[%s]\n",leaf->GetTypeName(), stars,
                              branchname,len,dimensions,leafcountName);
		fprintf(fp_idl," [%s size_is(%d,%s)]  %s %s%s[%d]%s;   //[%s]\n",
			get_attr(branchname),
			len,dimensions,
			get_type(leaf->GetTypeName()), stars,
			branchname,len,dimensions,leafcountName);
		add_b(v_val,branchname);
		add_b(v_ind,dimensions);
	    }
            delete [] dimensions;
         } else {
	    fprintf(fp," //:11\n");
            if (kmax) {
		fprintf(fp,"   %-14s %s%s[kMax%s];   //[%s]\n",leaf->GetTypeName(), stars, branchname,blen,leafcountName);
		fprintf(fp_idl," [%s size_is(%s)]  %s %s%s[kMax%s];   //[%s]\n",
			get_attr(branchname),
			leafcountName,
			get_type(leaf->GetTypeName()), stars, branchname,blen,leafcountName);
		add_b(v_val,branchname);
		add_b(v_ind,leafcountName);
	    }
            else      {
		fprintf(fp,"   %-14s %s%s[%d];   //[%s]\n",leaf->GetTypeName(), stars, branchname,len,leafcountName);
		fprintf(fp_idl," [%s size_is(%s)]  %s %s%s[%d];   //[%s]\n",
			get_attr(branchname),
			leafcountName,
			get_type(leaf->GetTypeName()), stars, branchname,len,leafcountName);
		add_b(v_val,branchname);
		add_b(v_ind,leafcountName);
	    }
         }
         if (stars[0]=='*') {
            TNamed *n;
            if (kmax) n = new TNamed(branchname, Form("kMax%s",blen));
            else n = new TNamed(branchname, Form("%d",len));
            mustInitArr.Add(n);
         }
      } else {
         if (strstr(branchname,"[")) len = 1;
	 fprintf(fp," //:12\n");
         if (len < 2) {
	     fprintf(fp," //:12-1\n");
	     fprintf(fp,"   %-15s %s;\n",leaf->GetTypeName(), branchname);
	     fprintf(fp_idl,"  [%s] %s %s;\n",get_attr(branchname),get_type(leaf->GetTypeName()), branchname);
	     add_b(v_val,branchname);
	 }
         else {
            if (twodim) {
		fprintf(fp," //:12-2\n");
		fprintf(fp,"   %-15s %s%s;\n",leaf->GetTypeName(), branchname,
			(char*)strstr(leaf->GetTitle(),"["));
	    }
            else       {
		fprintf(fp," //:12-3\n");
	       	fprintf(fp,"   %-15s %s[%d];\n",leaf->GetTypeName(),
		    branchname,len);
	       	fprintf(fp_idl,"  [%s] %s %s[%d];\n",get_attr(branchname),get_type(leaf->GetTypeName()),
		    branchname,len);
		add_b(v_val,branchname);
	    }
         }
      }
   }

// generate list of branches
   fprintf(fp,"\n");
   fprintf(fp,"   // List of branches\n");
   for (l=0;l<nleaves;l++) {
      if (leafStatus[l]) continue;
      TLeaf *leaf = (TLeaf*)leaves->UncheckedAt(l);
      fprintf(fp,"   TBranch        *b_%s;   //!\n",R__GetBranchPointerName(leaf).Data());
   }

// generate class member functions prototypes
   if (opt.Contains("selector")) {
      fprintf(fp,"\n");
      fprintf(fp,"   %s(TTree * /*tree*/ =0) { }\n",classname) ;
      fprintf(fp,"   virtual ~%s() { }\n",classname);
      fprintf(fp,"   virtual Int_t   Version() const { return 2; }\n");
      fprintf(fp,"   virtual void    Begin(TTree *tree) {};\n");
      fprintf(fp,"   virtual void    SlaveBegin(TTree *tree) {};\n");
      fprintf(fp,"   virtual void    Init(TTree *tree);\n");
      fprintf(fp,"   virtual Bool_t  Notify();\n");
      fprintf(fp,"   virtual Bool_t  Process(Long64_t entry) {return kTRUE;};\n");
      fprintf(fp,"   virtual Int_t   GetEntry(Long64_t entry, Int_t getall = 0) { return fChain ? fChain->GetTree()->GetEntry(entry, getall) : 0; }\n");
      fprintf(fp,"   virtual void    SetOption(const char *option) { fOption = option; }\n");
      fprintf(fp,"   virtual void    SetObject(TObject *obj) { fObject = obj; }\n");
      fprintf(fp,"   virtual void    SetInputList(TList *input) { fInput = input; }\n");
      fprintf(fp,"   virtual TList  *GetOutputList() const { return fOutput; }\n");
      fprintf(fp,"   virtual void    SlaveTerminate() {};\n");
      fprintf(fp,"   virtual void    Terminate() {};\n\n");
      fprintf(fp,"   ClassDef(%s,0);\n",classname);
      fprintf(fp,"};\n");
      fprintf(fp,"\n");
      fprintf(fp,"#endif\n");
      fprintf(fp,"\n");
   } else {
      fprintf(fp,"\n");
      fprintf(fp,"   %s(TTree *tree=0);\n",classname);
      fprintf(fp,"   virtual ~%s();\n",classname);
      fprintf(fp,"   virtual Int_t    Cut(Long64_t entry);\n");
      fprintf(fp,"   virtual Int_t    GetEntry(Long64_t entry);\n");
      fprintf(fp,"   virtual Long64_t LoadTree(Long64_t entry);\n");
      fprintf(fp,"   virtual void     Init(TTree *tree);\n");
      fprintf(fp,"   virtual void     Loop();\n");
      fprintf(fp,"   virtual Bool_t   Notify();\n");
      fprintf(fp,"   virtual void     Show(Long64_t entry = -1);\n");
      fprintf(fp,"};\n");
      fprintf(fp,"\n");
      fprintf(fp,"#endif\n");
      fprintf(fp,"\n");
   }
// generate code for class constructor
   fprintf(fp,"#ifdef %s_cxx\n",classname);
   if (!opt.Contains("selector")) {
      fprintf(fp,"%s::%s(TTree *tree)\n",classname,classname);
      fprintf(fp,"{\n");
      fprintf(fp,"// if parameter tree is not specified (or zero), connect the file\n");
      fprintf(fp,"// used to generate this class and read the Tree.\n");
      fprintf(fp,"   if (tree == 0) {\n");
      if (ischain) {
         fprintf(fp,"\n#ifdef SINGLE_TREE\n");
         fprintf(fp,"      // The following code should be used if you want this class to access\n");
         fprintf(fp,"      // a single tree instead of a chain\n");
      }
      if (isHbook) {
         fprintf(fp,"      THbookFile *f = (THbookFile*)gROOT->GetListOfBrowsables()->FindObject(\"%s\");\n",
                    treefile.Data());
         fprintf(fp,"      if (!f) {\n");
         fprintf(fp,"         f = new THbookFile(\"%s\");\n",treefile.Data());
         fprintf(fp,"      }\n");
         Int_t hid;
         sscanf(fTree->GetName(),"h%d",&hid);
         fprintf(fp,"      tree = (TTree*)f->Get(%d);\n\n",hid);
      } else {
         fprintf(fp,"      TFile *f = (TFile*)gROOT->GetListOfFiles()->FindObject(\"%s\");\n",treefile.Data());
         fprintf(fp,"      if (!f) {\n");
         fprintf(fp,"         f = new TFile(\"%s\");\n",treefile.Data());
         if (gDirectory != gFile) {
            fprintf(fp,"         f->cd(\"%s\");\n",gDirectory->GetPath());
         }
         fprintf(fp,"      }\n");
         fprintf(fp,"      tree = (TTree*)gDirectory->Get(\"%s\");\n\n",fTree->GetName());
      }
      if (ischain) {
         fprintf(fp,"#else // SINGLE_TREE\n\n");
         fprintf(fp,"      // The following code should be used if you want this class to access a chain\n");
         fprintf(fp,"      // of trees.\n");
         fprintf(fp,"      TChain * chain = new TChain(\"%s\",\"%s\");\n",
                 fTree->GetName(),fTree->GetTitle());
         TIter next(((TChain*)fTree)->GetListOfFiles());
         TChainElement *element;
         while ((element = (TChainElement*)next())) {
            fprintf(fp,"      chain->Add(\"%s/%s\");\n",element->GetTitle(),element->GetName());
         }
         fprintf(fp,"      tree = chain;\n");
         fprintf(fp,"#endif // SINGLE_TREE\n\n");
      }
      fprintf(fp,"   }\n");
      fprintf(fp,"   Init(tree);\n");
      fprintf(fp,"}\n");
      fprintf(fp,"\n");
   }

// generate code for class destructor()
   if (!opt.Contains("selector")) {
      fprintf(fp,"%s::~%s()\n",classname,classname);
      fprintf(fp,"{\n");
      fprintf(fp,"   if (!fChain) return;\n");
      if (isHbook) {
         //fprintf(fp,"   delete fChain->GetCurrentFile();\n");
      } else {
         fprintf(fp,"   delete fChain->GetCurrentFile();\n");
      }
      fprintf(fp,"}\n");
      fprintf(fp,"\n");
   }
// generate code for class member function GetEntry()
   if (!opt.Contains("selector")) {
      fprintf(fp,"Int_t %s::GetEntry(Long64_t entry)\n",classname);
      fprintf(fp,"{\n");
      fprintf(fp,"// Read contents of entry.\n");

      fprintf(fp,"   if (!fChain) return 0;\n");
      fprintf(fp,"   return fChain->GetEntry(entry);\n");
      fprintf(fp,"}\n");
   }
// generate code for class member function LoadTree()
   if (!opt.Contains("selector")) {
      fprintf(fp,"Long64_t %s::LoadTree(Long64_t entry)\n",classname);
      fprintf(fp,"{\n");
      fprintf(fp,"// Set the environment to read one entry\n");
      fprintf(fp,"   if (!fChain) return -5;\n");
      fprintf(fp,"   Long64_t centry = fChain->LoadTree(entry);\n");
      fprintf(fp,"   if (centry < 0) return centry;\n");
      fprintf(fp,"   if (!fChain->InheritsFrom(TChain::Class()))  return centry;\n");
      fprintf(fp,"   TChain *chain = (TChain*)fChain;\n");
      fprintf(fp,"   if (chain->GetTreeNumber() != fCurrent) {\n");
      fprintf(fp,"      fCurrent = chain->GetTreeNumber();\n");
      fprintf(fp,"      Notify();\n");
      fprintf(fp,"   }\n");
      fprintf(fp,"   return centry;\n");
      fprintf(fp,"}\n");
      fprintf(fp,"\n");
   }

// generate code for class member function Init(), first pass = get branch pointer
   fprintf(fp,"void %s::Init(TTree *tree)\n",classname);
   fprintf(fp,"{\n");
   fprintf(fp,"   // The Init() function is called when the selector needs to initialize\n"
              "   // a new tree or chain. Typically here the branch addresses and branch\n"
              "   // pointers of the tree will be set.\n"
              "   // It is normally not necessary to make changes to the generated\n"
              "   // code, but the routine can be extended by the user if needed.\n"
              "   // Init() will be called many times when running on PROOF\n"
              "   // (once per file to be processed).\n\n");
   if (mustInit.Last()) {
      TIter next(&mustInit);
      TObject *obj;
      fprintf(fp,"   // Set object pointer\n");
      while( (obj = next()) ) {
         if (obj->InheritsFrom(TBranch::Class())) {
            strcpy(branchname,((TBranch*)obj)->GetName() );
         } else if (obj->InheritsFrom(TLeaf::Class())) {
            strcpy(branchname,((TLeaf*)obj)->GetName() );
         }
         bname = branchname;
         while (*bname) {
            if (*bname == '.') *bname='_';
            if (*bname == ',') *bname='_';
            if (*bname == ':') *bname='_';
            if (*bname == '<') *bname='_';
            if (*bname == '>') *bname='_';
            bname++;
         }
         fprintf(fp,"   %s = 0;\n",branchname );
      }
   }
   if (mustInitArr.Last()) {
      TIter next(&mustInitArr);
      TNamed *info;
      fprintf(fp,"   // Set array pointer\n");
      while( (info = (TNamed*)next()) ) {
         fprintf(fp,"   for(int i=0; i<%s; ++i) %s[i] = 0;\n",info->GetTitle(),info->GetName());
      }
      fprintf(fp,"\n");
   }
   fprintf(fp,"   // Set branch addresses and branch pointers\n");
   fprintf(fp,"   if (!tree) return;\n");
   fprintf(fp,"   fChain = tree;\n");
   if (!opt.Contains("selector")) fprintf(fp,"   fCurrent = -1;\n");
   fprintf(fp,"   fChain->SetMakeClass(1);\n");
   fprintf(fp,"\n");
   for (l=0;l<nleaves;l++) {
      if (leafStatus[l]) continue;
      TLeaf *leaf = (TLeaf*)leaves->UncheckedAt(l);
      len = leaf->GetLen();
      leafcount =leaf->GetLeafCount();
      TBranch *branch = leaf->GetBranch();
      strcpy(aprefix,branch->GetName());

      if ( branch->GetNleaves() > 1) {
         // More than one leaf for the branch we need to distinguish them
         strcpy(branchname,branch->GetName());
         strcat(branchname,".");
         strcat(branchname,leaf->GetTitle());
         if (leafcount) {
            // remove any dimension in title
            char *dim =  (char*)strstr(branchname,"["); if (dim) dim[0] = 0;
         }
      } else {
         strcpy(branchname,branch->GetName());
         if (branch->IsA() == TBranchElement::Class()) {
            bre = (TBranchElement*)branch;
            if (bre->GetType() == 3 || bre->GetType()==4) strcat(branchname,"_");
         }
      }
      bname = branchname;
      char *brak = strstr(branchname,"[");     if (brak) *brak = 0;
      char *twodim = (char*)strstr(bname,"["); if (twodim) *twodim = 0;
      while (*bname) {
         if (*bname == '.') *bname='_';
         if (*bname == ',') *bname='_';
         if (*bname == ':') *bname='_';
         if (*bname == '<') *bname='_';
         if (*bname == '>') *bname='_';
         bname++;
      }
      if (branch->IsA() == TBranchObject::Class()) {
         if (branch->GetListOfBranches()->GetEntriesFast()) {
            fprintf(fp,"   fChain->SetBranchAddress(\"%s\",(void*)-1,&b_%s);\n",branch->GetName(),R__GetBranchPointerName(leaf).Data());
            continue;
         }
         strcpy(branchname,branch->GetName());
      }
      if (branch->IsA() == TBranchElement::Class()) {
         if (((TBranchElement*)branch)->GetType() == 3) len =1;
         if (((TBranchElement*)branch)->GetType() == 4) len =1;
      }
      if (leafcount) len = leafcount->GetMaximum()+1;
      if (len > 1) fprintf(fp,"   fChain->SetBranchAddress(\"%s\", %s, &b_%s);\n",
                           branch->GetName(), branchname, R__GetBranchPointerName(leaf).Data());
      else         fprintf(fp,"   fChain->SetBranchAddress(\"%s\", &%s, &b_%s);\n",
                           branch->GetName(), branchname, R__GetBranchPointerName(leaf).Data());
   }
   //must call Notify in case of MakeClass
   if (!opt.Contains("selector")) {
      fprintf(fp,"   Notify();\n");
   }

   fprintf(fp,"}\n");
   fprintf(fp,"\n");

// generate code for class member function Notify()
   fprintf(fp,"Bool_t %s::Notify()\n",classname);
   fprintf(fp,"{\n");
   fprintf(fp,"   // The Notify() function is called when a new file is opened. This\n"
              "   // can be either for a new TTree in a TChain or when when a new TTree\n"
              "   // is started when using PROOF. It is normally not necessary to make changes\n"
              "   // to the generated code, but the routine can be extended by the\n"
              "   // user if needed. The return value is currently not used.\n\n");
   fprintf(fp,"   return kTRUE;\n");
   fprintf(fp,"}\n");
   fprintf(fp,"\n");

// generate code for class member function Show()
   if (!opt.Contains("selector")) {
      fprintf(fp,"void %s::Show(Long64_t entry)\n",classname);
      fprintf(fp,"{\n");
      fprintf(fp,"// Print contents of entry.\n");
      fprintf(fp,"// If entry is not specified, print current entry\n");

      fprintf(fp,"   if (!fChain) return;\n");
      fprintf(fp,"   fChain->Show(entry);\n");
      fprintf(fp,"}\n");
   }
// generate code for class member function Cut()
   if (!opt.Contains("selector")) {
      fprintf(fp,"Int_t %s::Cut(Long64_t entry)\n",classname);
      fprintf(fp,"{\n");
      fprintf(fp,"// This function may be called from Loop.\n");
      fprintf(fp,"// returns  1 if entry is accepted.\n");
      fprintf(fp,"// returns -1 otherwise.\n");

      fprintf(fp,"   return 1;\n");
      fprintf(fp,"}\n");
   }
   fprintf(fp,"#endif // #ifdef %s_cxx\n",classname);

   delete [] leafStatus;

   fprintf(fp_idl,"};\n");
   fclose(fp);//No 1
   fclose(fp_idl);

/* // changed to sh/sed/awk extracting
   for(int i=0; i < v_val.size(); i++) {
       bool exclude=false;
       for(int k=0; k < v_ind.size(); k++) {
	   if( v_ind[k] == v_val[i])
	       exclude = true;
       }
       if( !exclude ) {
	   obj_add_val(fp_ocl,v_val[i].data());
       }
   }
*/
   fprintf(fp_ocl,"end\n");
   printf("end1\n");
   fclose(fp_ocl);
   printf("end2\n");

//   thead.Data();
  // name_idl.Data();
   //fTree->GetName();
   Info("MakeOcaml","Files: %s, %s"
	  " generated from TTree: %s",
	   thead.Data(),
	   name_idl.Data(),
	   fTree->GetName());
   printf("end\n");
   return 0;
}

void print_usage(char *a)
{
   printf("\nUsage:\n./%s file.root ttree_name [-s] \n",a);
   printf("option -s means saving codes in /tmp/___ (for debug)\n");
}
void complain_usage(char *a)
{
    print_usage(a);
    exit(10);
}

int oldmain(int argc, char * argv[])
{
   if( argc < 3 )
   {
       print_usage(argv[0]);
       return 10;
   }
   TFile *f = new TFile(argv[1],"r");
   if (!f || f->IsZombie())
   {
       printf("file not found or corrupt\n");
       print_usage(argv[0]);
       return 11;
   }
   TTree *tree = (TTree *) f->Get(argv[2]);
   if  (!tree)
   {
       printf("ttree not found in file\n");
       return 12;
   }
   bool save_tmp = false;
   bool only_print_record = false;
   for(int i = 3; i<argc; i++)
   {
       if(!strcmp(argv[i],"-s"))
       {
	   save_tmp=true;
       }
       else if(!strcmp(argv[i],"-p"))
       {
	   only_print_record = true;
       }
       else
       {
	   printf("unrecognized option: %s\n",argv[3]);
	   print_usage(argv[0]);
	   return 13;
       }
   }
   printf("ttree found. Start processing...\n");
   char dir_name[80]="/tmp/TTree_OCaml_XXXXXX";
   char *tmp_dir = mkdtemp(dir_name);
   printf("making temporary directory \"%s\"\n",tmp_dir);
   mkdir(tmp_dir,0755);
   TString copy;
   copy.Form("cp Ocaml_dbm.ml Db_dir.ml ttree_creation.C c_fill_record.c ttree_decl_stubs.c ocaml_creation.ml ttree_declLinkDef.h Makefile.basic gen_class.sh %s",tmp_dir);
   gSystem->Exec(copy);
//   copy.Form("cp rootlogon.C %s",tmp_dir);
//   gSystem->Exec(copy);
//   printf("%s\n",copy.Data());
   char *old_dir=getcwd(NULL,0);

   chdir(tmp_dir);
   printf("make class:\n");
   MakeClass(tree,"ttree_decl","ttree_analog.idl","selector");

   TString make_command; make_command.Form("make -f Makefile.basic ttree_analog.ml");
   gSystem->Exec(make_command);

   char *file = basename(argv[1]);
   if(!only_print_record)
   {
     TString make_command; make_command.Form("make -f Makefile.basic ");
     gSystem->Exec(make_command);
  
     TString exe_file; exe_file.Form("fill_%s_%s",file,argv[2]);
     copy.Form("cp fill %s/%s",old_dir,exe_file.Data());
     printf("%s\n",copy.Data());
     gSystem->Exec(copy);
  
     chdir(old_dir);
     TString run; run.Form("./%s %s %s",exe_file.Data(),argv[1],argv[2]);
     printf("\nBegin conversion TTree->Ocaml:\n\n%s\n",run.Data());
     gSystem->Exec(run);
   }

   // gen. oclassdef.ml
   gSystem->Exec("sh gen_class.sh");

   chdir(tmp_dir);
   char add[] = "cat oclassdef.ml >> ttree_analog.ml";
   printf(add);
   gSystem->Exec(add);

   copy.Form("cp ttree_analog.ml %s_%s/ttree_analog.ml",argv[1],argv[2]);
   printf("\n%s\n",copy.Data());
   gSystem->Exec(copy);

   copy.Form("cp ttree_analog.ml %s/ttree_analog_%s_%s.ml",old_dir,file,argv[2]);
   printf("\n%s\n",copy.Data());
   gSystem->Exec(copy);

   if(!save_tmp)
   {
       TString remove; remove.Form("rm -rf %s",tmp_dir);
       printf("%s\n",remove.Data());
       gSystem->Exec(remove);
   }

   return 0;
}

int main(int argc, char * argv[])
{
   if( argc < 2 )
   {
       complain_usage(argv[0]);
   }
   TFile *f = new TFile(argv[1],"r");
   if (!f || f->IsZombie())
   {
       printf("file not found or corrupt\n");
       complain_usage(argv[0]);
   }
   TTree *tree = (TTree *) f->Get(argv[2]);
   if  (!tree)
   {
       printf("ttree not found in file\nfile contents:\n");
       f->ls();
       return 12;
   }
   TString name_idl;
   name_idl.Form("ttree_analog.idl");
   for(int i = 3; i<argc; i++)
   {
       if(!strcmp(argv[i],"-idl"))
       {
	   if(i+1==argc) complain_usage(argv[0]);
	   name_idl.Form(argv[i+1]);
	   i += 1;
       }
       else
       {
	   printf("unrecognized option: %s\n",argv[i]);
	   print_usage(argv[0]);
	   return 13;
       }
   }
   printf("make class:\n");
   MakeClass(tree,"ttree_decl_$(TA_DECL)",name_idl,"selector");
   return 0;
}
